﻿using System;

namespace 面向对象_事件event
{
    class Program
    {
        static void Main(string[] args)
        {
            TestEvent();
        }
        private static void TestEventV2()
        {
            var school = new School3();
            var teacher = new Teacher2("小小");
            var teacher2 = new Teacher2("欧阳");
            school.OnBellRing += teacher2.Listen;
            //测试拦截多次添加方法到事件的能力
            // add  remove 访问器 不同于 get set 访问器，延续了 事件（委托）通过 += 进行多播的语法
            // get set 实现的委托属性如果 += 则添加 2次（意味着执行2次）
            // add 、remove 、value 套件 专用于 事件，延续了我们使用 += 、-= 来操作事件的习惯，+= 不会出现 上述情况。
            school.OnBellRing += teacher.Listen;
            school.OnBellRing += teacher.Listen;
            school.OnBellRing += teacher.Listen;
            school.OnBellRing += teacher.Listen;
            school.OnBellRing -= teacher.Listen;
            school.OnBellRing -= teacher.Listen;
            school.OnBellRing -= teacher.Listen;
            school.RingTheBell(Bell.BeginClass);
            school.OnBellRing -= teacher2.Listen;
            school.RingTheBell(Bell.BeginClass);


        }

        private static void TestEvent()
        {
            var school = new School();
            var teacher = new Teacher("小林");
            var student = new Student("李同学");
            school.OnClassBeginBellRing += teacher.BeginTeach;
            school.OnClassEndBellRing += teacher.EndTeach;
            school.OnClassEndBellRing += student.AttendClass;
            school.OnClassBeginBellRing += student.ClassisOver;

            // 事件不是谁想调就能调，它由事件发布者引发，相对之前学习的委托更安全， 如果想让委托安全加 event 。
            // school.OnClassBeginBellRing.Invoke();
            student.Listen(Bell.BeginClass);
            school.RingTheBell(Bell.EndClass);
            student.Listen(Bell.EndClass);
            Console.WriteLine(" 过去了  45 分钟...");
            school.RingTheBell(Bell.BeginClass);
            student.Listen(Bell.BeginClass);

            Console.WriteLine(" 另一所学校");

            var school2 = new School2();
            var teacher2 = new Teacher2("欧阳");

            school2.OnBellRing += teacher2.Listen;

            // 真巧，两所学校挨得很近，小林老师总是能够听到隔壁学校铃声
            school2.OnBellRing += teacher.Listen;

            school2.RingTheBell(Bell.BeginClass);
            Console.WriteLine(" 过去了  45 分钟...");
            school2.RingTheBell(Bell.EndClass);
        }
    }

    // event ：事件，一件事（发生的时刻），事件发生时会产生影响，受影响的是关心这个事件的事物
    // 吃瓜群众不止一个，事件的关心者也不会只停留在一个这样的数量级上

    //  事件是类或者类的实例（对象）向其他类或者类的实例通知发生的事情。
    //  事件的发送方：发行者 、发布
    //  事件的接受方：订阅者 
    //  事件是基于委托

    // 事件发布者确定何时引发事件；订阅者确定对事件做出何种反应
    //  一个事件有多个订阅者
    //  订阅者可以处理多个发行者的多个事件
    //  没有订阅者，事件永远不会引发（事件没有订阅者会变为 null ，没法执行）
    //  事件通常用于表示用户的操作：例如软件中点击一个按钮（就是触发了事件）

    //  事件的声明： 使用 event 关键字，形如：public event Action myevent
    //  事件的添加 ：事件名 += 要添加的方法名  ; 形如：myevent+=MyEvent;
    //  事件的取消： 事件名 -= 要取消的方法名； 形如： myevent -= MyEvent； 取消所有方法后，事件会置为 null 
    //  事件的调用： 事件名(); 推荐 事件名?.Invoke(), Invoke(里面写上参数)

    // school :学校
    class School
    {
        public delegate void BellRing();
        /// <summary>
        /// 当上课铃声响起的时候
        /// </summary>
        public event BellRing OnClassBeginBellRing;
        /// <summary>
        /// 当下课铃声响起的时候
        /// </summary>
        public event BellRing OnClassEndBellRing;

        public void RingTheBell(Bell bell)
        {

            switch (bell)
            {
                case Bell.EndClass:
                    OnClassEndBellRing?.Invoke();
                    break;

                case Bell.BeginClass:
                    OnClassBeginBellRing?.Invoke();
                    break;
            }
        }
    }

    enum Bell
    {
        /// <summary>上课铃</summary>
        EndClass,
        /// <summary>下课铃</summary>
        BeginClass
    }

    class Teacher
    {
        // readonly 只读 。 read:读  only ：只有，只要
        readonly string name;
        public Teacher(string name) => this.name = name;

        /// <summary>
        ///  开始教学
        /// </summary>
        public void BeginTeach()
        {
            Console.WriteLine($"上课铃响铃了，{name} 开始授课");
        }

        /// <summary>
        /// 结束教学
        /// </summary>
        public void EndTeach()
        {
            Console.WriteLine($"下课铃响铃了，{name} 结束授课");
        }

        public void Listen(Bell bell)
        {
            Console.WriteLine($"{name}老师: 学校铃声怎么变小了，并且乱敲一顿~");
        }
    }

    class School2
    {
        public event Action<Bell> OnBellRing;
        public void RingTheBell(Bell bell) => OnBellRing?.Invoke(bell);
    }
    class Teacher2
    {
        string name;
        public Teacher2(string name) => this.name = name;
        // listen :听
        public void Listen(Bell bell)
        {
            switch (bell)
            {
                case Bell.EndClass:
                    Console.WriteLine($"下课铃响起，{name} 结束授课 ");
                    break;
                case Bell.BeginClass:
                    Console.WriteLine($"上课铃响起，{name} 开始授课 ");
                    break;
            }
        }
        public override string ToString() => $"{name} 老师";

    }

    class School3
    {
        event Action<Bell> mBellRing;
        public event Action<Bell> OnBellRing
        {
            add
            {
                var list = mBellRing?.GetInvocationList();
                if (list == null || !Array.Exists(list, v => v.Target == value.Target && v.Method.Name == value.Method.Name))
                {
                    mBellRing += value;
                }
                else if (null != list)
                {
                    Console.WriteLine($"报告 {value.Target} ，事件中已经存在 {value.Method.Name} 方法 ，请勿重复添加！");
                }
            }
            remove
            {
                var list = mBellRing?.GetInvocationList();
                if (list != null && Array.Exists(list, v => v.Target == value.Target && v.Method.Name == value.Method.Name))
                {
                    mBellRing -= value;
                }
                else if (list != null)
                {
                    Console.WriteLine($"报告 {value.Target} ，事件从未监听 {value.Method.Name} 方法，移除失败");
                }
                if (mBellRing == null)
                {
                    Console.WriteLine($"{nameof(OnBellRing)}监听的事件全部被移除，一个不留！");
                }
            }
        }

        public void RingTheBell(Bell bell) => mBellRing?.Invoke(bell);
    }
    //只写一个学校的学生听到铃声的行为，事件不允许多次订阅
    class Student
    {
        // readonly 只读 。 read:读  only ：只有，只要
        public string name;
        public Student(string name) => this.name = name;
        public void Listen(Bell bell)
        {
            switch (bell)
            {
                case Bell.EndClass:
                    Console.WriteLine($"下课铃响起！，{name}结束上课");
                    break;
                case Bell.BeginClass:
                    Console.WriteLine($"上课铃响起！，{name}开始上课");
                    break;
            }
        }
        /// <summary>
        /// 上课
        /// </summary>
        public void AttendClass()
        {
            Console.WriteLine($"{name}，开始认真上课");
        }
        /// <summary>
        /// 下课
        /// </summary>
        public void ClassisOver()
        {
            Console.WriteLine($"{name},下课喽！");
        }

    }
}